/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Huawei Technologies
 *
 */

#include "arch/perf.h"

#include <signal.h>

#include <lwip/stats.h>

#if LWIP_RECORD_PERF

#define SIG_FUNC_NUM      3

#define SIG_STATS_DISPLAY 38
#define SIG_PERF_BEGIN    39
#define SIG_PERF_END      40

typedef void (*pSignalFunc) (int);
static void signal_stats_display(int s);
static void signal_perf_begin(int s);
static void signal_perf_end(int s);

uint32_t g_record_perf;
__thread uint64_t g_timeTaken[PERF_POINT_END];
__thread int g_perfPoint[PERF_LAYER_END];
__thread struct timespec tvStart[PERF_LAYER_END];
volatile uint64_t g_perfMaxtime[PERF_POINT_END];
volatile uint64_t g_astPacketCnt[PERF_POINT_END];
volatile uint64_t g_astPacketProcTime[PERF_POINT_END];

char *g_ppLayerName[PERF_POINT_END] = {
    "IP_RECV",
    "TCP_DATA_RECV",
    "UDP_PARTIAL",
    "TCP_SYN_RECV",
    "TCP_SYN_ACK_SEND",
    "TCP_ACK_RECV",
    "TCP_SYN_SEND",
    "TCP_SYN_ACK_RECV",
    "TCP_ACK_SEND",
    "TCP_DATA_SEND",
    "IP_SEND"
};

static int gsig_arr[SIG_FUNC_NUM] = {
    SIG_STATS_DISPLAY,
    SIG_PERF_BEGIN,
    SIG_PERF_END
};

static pSignalFunc g_Funcs[SIG_FUNC_NUM] = {
    signal_stats_display,
    signal_perf_begin,
    signal_perf_end,
};

static void print_perf_data_and_reset()
{
    int i;
    printf("\n********* PERF DATA START*************\n");
    for (i = 0; i < PERF_POINT_END; i++) {
        printf("%-20s Total:     PacketProcTime: %-15"PRIu64", Maxtime: %-15"PRIu64", packetCnt: %-15"PRIu64"\n",
            g_ppLayerName[i], __sync_fetch_and_or(&g_astPacketProcTime[i], 0),
            __sync_fetch_and_or(&g_perfMaxtime[i], 0),
            __sync_fetch_and_or(&g_astPacketCnt[i], 0));

        if (__sync_fetch_and_or(&g_astPacketProcTime[i], 0) && __sync_fetch_and_or(&g_astPacketCnt[i], 0)) {
        printf("%-20s Average:   PacketProcTime: %-15lf, MaxTime: %-15"PRIu64"\n", g_ppLayerName[i],
            (double)__sync_fetch_and_or(&g_astPacketProcTime[i], 0) / (double)__sync_fetch_and_or(&g_astPacketCnt[i], 0),
            __sync_or_and_fetch(&g_perfMaxtime[i], 0));
        }

        __sync_fetch_and_and (&g_astPacketProcTime[i], 0);
        __sync_fetch_and_and (&g_astPacketCnt[i], 0);
        __sync_fetch_and_and (&g_perfMaxtime[i], 0);
    }
    printf("\n********* PERF DATA END*************\n");
}

static void signal_stats_display(int s)
{
    struct sigaction s_test;
    printf("Received signal %d, stats display.\n", s);
    stats_display();
    s_test.sa_handler = (void *) signal_stats_display;
    if (sigemptyset(&s_test.sa_mask) != 0) {
        printf("sigemptyset failed.\n");
    }
    s_test.sa_flags = SA_RESETHAND;
    if (sigaction(s, &s_test, NULL) != 0) {
        printf("Could not register %d signal handler.\n", s);
    }
}

static void signal_perf_begin(int s)
{
    struct sigaction s_test;
    printf("Received signal %d, perf_begin.\n", s);
    g_record_perf = 1;
    s_test.sa_handler = (void *) signal_perf_begin;
    if (sigemptyset(&s_test.sa_mask) != 0) {
        printf("sigemptyset failed.\n");
    }
    s_test.sa_flags = SA_RESETHAND;
    if (sigaction(s, &s_test, NULL) != 0) {
        printf("Could not register %d signal handler.\n", s);
    }
}

static void signal_perf_end(int s)
{
    struct sigaction s_test;
    printf("Received signal %d, perf_end\n", s);
    g_record_perf = 0;
    print_perf_data_and_reset();
    s_test.sa_handler = (void *) signal_perf_end;
    if (sigemptyset(&s_test.sa_mask) != 0) {
        printf("sigemptyset failed.\n");
    }
    s_test.sa_flags = SA_RESETHAND;
    if (sigaction(s, &s_test, NULL) != 0) {
        printf("Could not register %d signal handler.\n", s);
    }
}

int check_layer_point(int layer, int point)
{
    if (point == g_perfPoint[layer]) {
        return 1;
    }
    return 0;
}

int perf_init(void)
{
    int i;
    struct sigaction s_test;
    for (i = 0; i < SIG_FUNC_NUM; i++) {
        s_test.sa_handler = (void *) g_Funcs[i];
        if (sigemptyset(&s_test.sa_mask) != 0) {
            printf("sigemptyset failed.\n");
            return 1;
        }

        s_test.sa_flags = SA_RESETHAND;
        if (sigaction(gsig_arr[i], &s_test, NULL) != 0) {
            printf("Could not register %d signal handler.\n", gsig_arr[i]);
            return 1;
        }
    }
    return 0;
}
#endif
